/***************************************************************************
 *   Copyright (C) 2009 - 2010 by Simon Qian <SimonQian@SimonQian.com>     *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "vsftimer.h"
#include "framework/vsfsm/vsfsm.h"

#define VSFSM_EVT_TIMER					8

static struct vsfsm_state_t *
vsftimer_init_handler(struct vsfsm_t *sm, vsfsm_evt_t evt);

struct vsftimer_t
{
	struct vsftimer_timer_t *timerlist;
	struct vsfsm_t sm;
} static vsftimer =
{
	NULL,
	{
		{
			vsftimer_init_handler,
		},								// struct vsfsm_state_t init_state;
	},
};


extern uint32_t get_system_tick(void);

// vsftimer_callback_int is called in interrupt,
// simply send event to vsftimer SM
void vsftimer_callback_int(void)
{
	vsfsm_post_evt_pending(&vsftimer.sm, VSFSM_EVT_TIMER);
}

static void insert_timer(struct vsftimer_timer_t *timer)
{
	struct vsftimer_timer_t *ptimer, *ntimer;

	sllist_init_node(timer->list);

	if (NULL == vsftimer.timerlist)
	{
		vsftimer.timerlist = timer;
		return;
	}

	if (vsftimer.timerlist->trigger_tick > timer->trigger_tick)
	{
		sllist_insert(timer->list, vsftimer.timerlist->list);
		vsftimer.timerlist = timer;
		return;
	}

	ptimer = vsftimer.timerlist;
	ntimer = sllist_get_container(ptimer->list.next, struct vsftimer_timer_t,
									list);
	while (ntimer != NULL)
	{
		if (ntimer->trigger_tick > timer->trigger_tick)
		{
			break;
		}
		ptimer = ntimer;
		ntimer = sllist_get_container(ptimer->list.next,
										struct vsftimer_timer_t, list);
	}

	if (ntimer != NULL)
	{
		sllist_insert(timer->list, ntimer->list);
	}
	sllist_insert(ptimer->list, timer->list);
}

vsf_err_t vsftimer_init(void)
{
	vsftimer.timerlist = NULL;
	return vsfsm_init(&vsftimer.sm);
}

vsf_err_t vsftimer_unregister(struct vsftimer_timer_t *timer)
{
	if (vsftimer.timerlist == NULL)
	{
		return VSFERR_FAIL;
	}

	if (vsftimer.timerlist == timer)
	{
		vsftimer.timerlist = sllist_get_container(timer->list.next,
								struct vsftimer_timer_t, list);
		return VSFERR_NONE;
	}
	return sllist_remove(&vsftimer.timerlist->list.next, &timer->list);
}

vsf_err_t vsftimer_register_cnt(struct vsftimer_timer_t *timer, int16_t cnt)
{
	if (timer->interval == 0)
	{
		return VSFERR_FAIL;
	}

	timer->trigger_tick = timer->interval + get_system_tick();
	timer->trigger_cnt = cnt;

	vsftimer_unregister(timer);
	insert_timer(timer);

	return VSFERR_NONE;
}

vsf_err_t vsftimer_register(struct vsftimer_timer_t *timer)
{
	return vsftimer_register_cnt(timer, -1);
}

static struct vsfsm_state_t *
vsftimer_init_handler(struct vsfsm_t *sm, vsfsm_evt_t evt)
{
	switch (evt)
	{
	case VSFSM_EVT_TIMER:
	{
		uint32_t cur_tick = get_system_tick();
		struct vsftimer_timer_t *timer = vsftimer.timerlist;

		while (timer != NULL)
		{
			if (cur_tick >= timer->trigger_tick)
			{
				// triggered
				timer->trigger_tick += timer->interval;
				vsftimer_unregister(timer);
				if (timer->trigger_cnt > 0)
				{
					timer->trigger_cnt--;
				}
				if (timer->trigger_cnt != 0)
				{
					insert_timer(timer);
				}
#if VSFSM_CFG_DELAY_EN
				else
				{
					timer->malloced = 0;
				}
#endif
				if ((timer->sm != NULL) && (timer->evt != VSFSM_EVT_INVALID))
				{
					vsfsm_post_evt(timer->sm, timer->evt);
				}
				timer = vsftimer.timerlist;
				continue;
			}
			else
			{
				break;
			}
		}
		return NULL;
	}
	default:
		return NULL;
	}
}

#if VSFSM_CFG_AUTO_WFI_EN
static void (*vsftimer_wfi_handle)(void);
static void (*vsftimer_wakeup_interval_set_handle)(uint32_t ms);

vsf_err_t vsftimer_wfi_init(void (*wakeup_interval_set_handle)(uint32_t ms),
							void (*wfi_handle)(void))
{
	if ((wakeup_interval_set_handle == NULL) || (wfi_handle == NULL))
	{
		return VSFERR_FAIL;
	}

	vsftimer_wakeup_interval_set_handle = wakeup_interval_set_handle;
	vsftimer_wfi_handle = wfi_handle;
	return VSFERR_NONE;
}

vsf_err_t vsftimer_wfi(void)
{
	if ((vsftimer_wakeup_interval_set_handle == NULL) ||
		(vsftimer_wfi_handle == NULL))
	{
		return VSFERR_FAIL;
	}

	if(vsftimer.timerlist == NULL)
	{
		(*vsftimer_wakeup_interval_set_handle)(0xffff);
	}
	else
	{
		uint32_t cur_tick = get_system_tick();
		if (vsftimer.timerlist->trigger_tick > cur_tick)
		{
			(*vsftimer_wakeup_interval_set_handle)(vsftimer.timerlist->trigger_tick - cur_tick);
		}
		else
		{
			return VSFERR_NOT_READY;
		}
	}
	(*vsftimer_wfi_handle)();
	return VSFERR_NONE;
}
#endif // VSFSM_CFG_AUTO_WFI_EN

#if VSFSM_CFG_DELAY_EN
static struct vsftimer_timer_t delay_timer_pool[VSFSM_CFG_DELAY_POOL_SIZE];
vsf_err_t vsftimer_delay_init(void)
{
	uint32_t i;

	if (VSFSM_CFG_DELAY_POOL_SIZE <= 0)
	{
		return VSFERR_NOT_ENOUGH_RESOURCES;
	}

	for (i = 0; i < VSFSM_CFG_DELAY_POOL_SIZE; i++)
	{
		delay_timer_pool[i].interval = 1;
		delay_timer_pool[i].sm = NULL;
		delay_timer_pool[i].evt = VSFSM_EVT_DELAY_DONE;
		delay_timer_pool[i].list.next = NULL;
		delay_timer_pool[i].trigger_tick = 0;
		delay_timer_pool[i].trigger_cnt = 1;
		delay_timer_pool[i].malloced = 0;
	}
	return VSFERR_NONE;
}

vsf_err_t vsftimer_delay(struct vsfsm_t *sm, uint32_t tick)
{
	uint32_t i;
	vsf_err_t err;

	if ((sm == NULL) || (tick == 0))
	{
		return VSFERR_INVALID_PARAMETER;
	}

	for (i = 0; i < VSFSM_CFG_DELAY_POOL_SIZE; i++)
	{
		if (delay_timer_pool[i].malloced == 0)
		{
			break;
		}
	}
	if (i >= VSFSM_CFG_DELAY_POOL_SIZE)
	{
		// if debug, need output error info
		return VSFERR_NOT_ENOUGH_RESOURCES;
	}
	delay_timer_pool[i].interval = tick;
	delay_timer_pool[i].sm = sm;
	delay_timer_pool[i].malloced = 1;

	err = vsftimer_register_cnt(&delay_timer_pool[i], 1);
	if (err)
	{
		delay_timer_pool[i].malloced = 0;
	}
	return err;
}
#endif

